2019年01月11日

本日の内容

昨年までの学びを振り返ってみましょう。

  1. なぜPythonでなくRを使うのか?
  2. Reproducible Research (再現可能性)
  3. Tidy Data (整然データ)
  4. Data Science Workflow を元に振り返る(注:取り留めないです)
    • Program
    • Import
    • Tidy/Transform
    • Visualize
    • Communicate

振り返り・落穂拾い

なぜPythonでなくRを使うのか?

Six Reasons To Learn R For Business, R Blogger

  1. R Has The Best Overall Qualities
  2. R Is Data Science For Non-Computer Scientists
  3. Learning R Is Easy With The Tidyverse
  4. R Has Brains, Muscle, And Heart
    • Cutting-edge algorithms and Powerful tools (packages)
  5. R Is Built For Business
    • R Markdown
  6. R Community Support

なぜPythonでなくRを使うのか?(その2)

統計言語 「R」のネ申はなぜ無償で貢献したのか (日経ビジネス 「会社とは何か」 Hadly Wickhamへのインタビュー記事)より抜粋

 
両者1に共通しているのはデータを扱うために言語や文法を定義しているところです。目標はできるだけはっきり、簡単にコードで表現すること。どちらもプロブラマー2でない人でもコードを書くことができます。それを理解するために専門家になる必要もない。文章のようにコードを読み、もっと改善できるところを指摘し、そのコードを考えてシェアしていくことができるように設計しました。他の人が何を理解しにくいと思うか、という心理的なことも考えて書いています

 

1 dplyrパッケージとggplot2パッケージのこと(総じてtidyverseの世界)
2 恐らく「プログラマー」の誤植

なぜPythonでなくRを使うのか?(その3)

統計言語 「R」のネ申はなぜ無償で貢献したのか (日経ビジネス 「会社とは何か」 Hadly Wickhamへのインタビュー記事)より抜粋

 
Rコミュニティでのやりとりの中で努力してきたことの一つは、できるだけ入ってきた人たちを歓迎し、心地よい場所にする3ということでした。Rを利用している人々はプログラマーではない人も多いですから、最初はとっつきにくい場合もあるでしょうし。(中略)世界中でフレンドリーなコミュニティが生まれてとてもうれしく思います。

 

3 Slackコミュニティの R-wakalang は、まさにこの精神で運用されいています(夏休みの宿題を質問しても答えてくれる)。

Reproducible Research

  • データから分析結果が再現できること
  • 同じデータから同じ結果が得られることは科学的知見の蓄積において重要
  • 過去の分析を見直した時に何をやっているか理解できることも重要
    • その場限りの分析では知見の蓄積にならない
  • 文書化(記録)されない手作業(データ整形、コピペなど)が再現性を阻害する
    • 手作業は、まず文書化(記録)されない
    • 人の記憶は忘却曲線と共に
    • 手作業では間違いが混入しやすい

Tidy Data

乱暴に言うと「Coddの第三正規形」を満たすデータ。対義語はMessy Data。

  • Each variable forms a column.
  • Each observation forms a row.
  • Each type of observational unit forms a table.
  • Each value is a cell.

日本語では以下で、構造と意味が一致しているデータをTidy Dataと呼びます。

  • 個々の変数が1つの列をなす
  • 個々の観測が1つの行をなす
  • 個々の観測の構成単位の類型が1つの表をなす
  • 個々の値が1つのセルをなす

Data Science Workflow

RStartHere

Each data science project is different, but each follows the same general steps.

Data Science Workflow, CC BY 4.0 RStudio

Program

開発環境

  • RのIDEはRStudioを使うのが現状ベスト
    • RやR Markdownのコーディングに最適化
    • プロジェクト単位でのファイル・パッケージ管理が可能
    • 充実したヘルプやトレース機能
    • Git/SVNなどのVCSとの連携によるソース管理など
  • 日本語環境を快適に使うのにWinodwsは(個人的には)非推奨
    • OS自体がいまだにSJIS(CP932)を利用しているので日本語処理に難がある
      • 日本語があると正しく処理できないケースが増えている
      • Windows環境ならDockerでRStudio Serverを動かす方がベターだが…
      • 近いうちにUTF-8ベースになる予定はあるが…
    • mac OS または linux(Ubuntu)などUTF-8ベースのOSでの利用を推奨
      • ストレスフリーに近い

RStuido Tips

RStudioを利用する場合はR MarkdownのR Notebook機能を活用すると実行結果を確認するためにknitする必要がなく分析が早く分かりやすくなります。

  • R Notebook機能 - R Markdownのchunkの下に実行結果を表示する機能

R Notebook機能を上手に活用するには[Ctrl + Enter]と[Ctrl + Shift + Enter]のショートカットキーを利用します。

  • [Ctrl + Enter] - カーソルがある部分、または、選択部分を実行する
  • [Ctrl + Shift + Enter] - カーソルがあるchunk全体を実行する

コードがパイプでつながっている場合は、[Ctrl + Enter]でも一連のコードを実行してくれます。また、ライブラリ読み込みなど共通処理はsetupchunkに記載すると便利です。

setup chunkの例

\```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)

require(DT)
require(dygraphs)
require(plotly)
require(tidyverse)    # tidyverseは最後に読み込むとコンフリクトが分かりやすい
\```

RStuidoエディタでR Markdownファイルの任意のchunkを実行した場合、最初の実行時のみ自動的にsetupのチャンクが実行され初期処理をしてくれる便利な機能。なお、実際に記述する際は先頭の\は記述しないでください。

コンソール出力のリダイレクト

RやRStudioのコンソール出力をファイルへリダイレクトしたい場合はsink関数を使ってください。ただし、R Markdownのチャンク内では有効に機能しません。

sink(file = "foo.log")

iris %>% 
  dplyr::filter(Species == "setosa") %>% 
  lm(Sepal.Length ~ Sepal.Width, data = .) %>% summary()

sink()

なお、コンソール入力(>で入力するコマンド)は出力されませんので、コンソール入力も合わせて出力したい場合はTeachingDemosパッケージを利用してください。

オープンソース

Rに限らず分析を行う場合にはオープンソース関連の知識も必要になってきています。

  • VCS(Version Control System)関連は必須
    • GitHub, GitLab, BitBucketなどのクラウドサービス
    • Git自体 or Git Client
  • SNS以外で情報発信したい場合
    • ホスティングサービス
      • 静的ホスティングサービス(GitHub Pages、Netlifyなど)
      • AWSなども使えるようになればベター
    • 静的サイトジェネレーター
      • hugoなど

Import

複数の連続したURLを読み込む

Kabutan のようにURLを用いてテーブルを切り替えているページ(https://kabutan.jp/stock/kabuka?code=0950&ashi=wek&page=n)をスクレイピングする場合にはpurrrパッケージを用いると便利です。

purrr::map2(.x = site_url, .y = c(1:n), .f = ~ paste0(.x, .y)) %>% 
                                             # URLリストを作成
  purrr::map_df(.x = ., .f = function(.x) {  # そのリストを元に関数(スクレイピング)を実行
    Sys.sleep(1)                             # Sleepは最初に入れるのが肝
    xml2::read_html(.x) %>%                  # ページと読み込み処理
      rvest::xml_node(xpath = xpath_yd) %>% # テーブル要素を抽出
      rvest::html_table()                    # テーブルをデータフレーム化
  })                     # データフレームを結合

purrrパッケージは並列処理ではありませんのでfor文と同様にSys.sleep関数で読み込み間隔を調整することができ、面倒な処理なしにデータフレームを結合することが可能です。

XPath

絶対パス(構成変更に弱い)と一意?パス(設計・実装が面倒?)

データストア

  • データストアに対してはSQLアクセスが利用できるのがベスト
    • SQLアクセスが利用できない場合はAPIアクセス
    • APIアクセスが利用できない場合はスクレイピング
    • スクレイピングが利用できない場合はファイル入力

 

  • データストアにファイルを使うならバージョン管理ができる形式で
    • 必ずソースと共にVCSで管理する(再現可能性を低下させない)
    • ファイルから読み込んだデータをSQLiteのようなサーバレスDBMSで管理する手もある
      • こちらはなれないと難しいが、データを追加していく際に便利

Importあれこれ

Tidy/Transform

データハンドリング

データハンドリングは「記録」を分析できるデータにするために必要な前処理。

  1. データクレンジング
    • データの中身を整えてデータの品質を上げる
      • 揺れやゴミ、重複データを取り除く、データを補完する
      • 任意の型に変換する(例:文字型を数値型など)
      • 正規表現が役に立ちます
  2. 整形処理
    • データの形を整えてコンピュータで処理できるようにする
      • 雑然データを整然データに変形する
      • 整然データを処理する(削除・抽出・要約・計算・集計など)
      • tidyverseの出番です

NAの扱い

データをインポートする際に情報がない部分はデフォルトで欠損値(NA)になりますが、NAを含む変数を条件処理する場合は注意が必要です。Excelのフィルター感覚で条件を指定してもNAは評価対象外になってしまいます。

data.frame(x = c("a", "b", NA, "a", "c", "b"), y = c(1:6)) %>% 
  dplyr::filter(x != "a")
NA != "a"       # NAの評価結果は常にNA
## [1] NA

NAの扱い(つづき)

これを解決する方法の一つに%in%演算子があります。%in%match関数のラッパー関数で、左辺の要素が右辺に存在するかを確認するための演算子です。これを応用すると左辺の要素から右辺で指定した要素を抽出することができます(返り値がBooleanベクトルなのでこれを利用します)。

data.frame(x = c("a", "b", NA, "a", "c", "b"), y = c(1:6)) %>% 
  dplyr::filter(!(x %in% c("a")))

NAの扱い(つづき)

なぜ%in%NAを拾えるかは%in%のコードを追うと分かります。

"%in%" <- function(x, table){ match(x, table, nomatch = 0) > 0 }
data.frame(x = c("a", "b", NA, "a", "c", "b"), y = c(1:6)) %>% 
  with(., !(match(x, c("a"), nomatch = 0) > 0))
## [1] FALSE  TRUE  TRUE FALSE  TRUE  TRUE

match関数の中でidentical関数(Test Objects for Exact Equality)が呼ばれていますので、NAでも一致しているか否かが判定できるのです。

identical(NA, "a")
## [1] FALSE

.の使い方

tidyverseのパイプ処理では.を使って明示的に前段の処理結果を関数の任意の引数に渡すことが可能です。例えば

ggplot2::mpg %>%
  dplyr::filter(manufacturer != "lincoln") %>% 
  oneway.test(cty ~ manufacturer, data = ., var = TRUE) %>% 
  .$p.value
## [1] 1.342687e-30

しかし、dplyr::group_byを用いる場合には注意が必要です。

.の使い方(つづき)

明示的に引数にデータを指定しようとして以下のような指定を行うとグルーピングされたデータが渡らずにグループ数だけ元のデータで計算されてしまいます(r-wakalangでやらかした…一応、やらかしたことは自分で気がついたけど)。これは%>%演算子の仕様だそうです。

ggplot2::mpg %>%
  dplyr::filter(manufacturer != "lincoln") %>% 
  dplyr::group_by(year) %>%
  summarise(result = oneway.test(cty ~ manufacturer, data = ., var = TRUE)$p.value)

.の使い方(つづき)

dplyr::group_byでグルーピングをする場合は以下のように.を使用しないでください。

ggplot2::mpg %>%
  dplyr::filter(manufacturer != "lincoln") %>% 
  dplyr::group_by(year) %>%
  summarise(result = oneway.test(cty ~ manufacturer, var = TRUE)$p.value)

もしくは、 purrrパッケージを用いる方法 を使ってください。

tidyrを振り返る

iris

tidyr::gather

tidyr::gather関数は、keyに変数名をvalueに値をまとめます。

iris %>% tidyr::gather(key = "key", value = "value", -Species) %>% head()

tidyr::spread

tidyr::spread関数はkeyを変数名にvalueをその値として展開しますが、行識別のためにユニークな値が必要です。

iris %>% tibble::rowid_to_column("id") %>% 
  tidyr::gather(key = "key", value = "value", -id, -Species) %>% 
  tidyr::spread(key = key, value = value) %>% dplyr::select(-id) %>% head()

表示順を変えたい(forcats)

iris %>% ggplot2::ggplot(ggplot2::aes(x = Species, y = Sepal.Width)) +
    ggplot2::geom_boxplot(ggplot2::aes(colour = Species))

中央値順に並べ替える(forcats)

iris %>% dplyr::mutate(Species = forcats::fct_reorder(Species, Sepal.Width)) %>%
  ggplot2::ggplot(ggplot2::aes(x = Species, y = Sepal.Width)) +
    ggplot2::geom_boxplot(ggplot2::aes(colour = Species))

purrrは何ができるのか?

purrr::map関数は指定した引数に対して指定した処理(関数、演算子など)を適用する関数で繰り返し処理に威力を発揮します。並列処理ではありません。例えば、

  • 多数のファイルを読込んで結合する
file_path %>% list.files(path = ., pattern = "(issues_)", full.names = TRUE) %>% 
  purrr::map_df(.x = ., .f = readr::read_csv, locale = readr::locale(encoding = "cp932"))
  • 因子水準ごとに処理を適用する
iris %>% split(.$Species) %>% 
  purrr::map_df(~ lm(Sepal.Length ~ Sepal.Width, data = .x) %>% broom::tidy(),
                .id = "Species")
  • データフレーム内のリストを処理する(第6回で実施)

purrr::map

第6回で説明したpurrr::map_df関数を使った カスタムフィールドの展開

with(issues, purrr::map_df(.x = custom_fields, .f = function(.x) {c(.x)}))

以下の処理と等価です(なお、c関数がなくても結果は同じです)。

dplyr::bind_rows(
  c(list(id = 2L, name = "Resolution", value = "Invalid")),
  c(list(id = 4L, name = "Affected version")) )

purrr::map2

第6回で説明したpurrr::map2_df関数を使った カスタムフィールドの展開

with(issues, purrr::map2_df(.x = id, .y = custom_fields,
                            .f = function(.x, .y) {c(issue_id = .x, .y)}))

以下の処理と等価です。

dplyr::bind_rows(
  c(issue_id = 16451L, list(id = 2L, name = "Resolution", value = "Invalid")),
  c(issue_id = 16451L, list(id = 4L, name = "Affected version")) )

dplyr, breaking change

最近はこのようにpurrrパッケージ押しなんですが、dplyrパッケージが今月に正式リリースされる予定の次バージョンからグルーピングの扱いが大きく変わり(まだまだ、実験的な実装も含まれているようですが)purrrパッケージを用いなくても効率的なグルーピング処理ができるようになりそうです。

 
これに伴いdplyr::doは完全に非推奨となるようです。

リリースされましたら使い勝手などを Blog に書くつもりです。

Tidy/Transformあれこれ

Visualize

ggplot2::aes、中に書くか外に書くか?

iris %>% ggplot2::ggplot(ggplot2::aes(x = Species, fill = Species)) + 
  ggplot2::geom_boxplot(ggplot2::aes(y = Sepal.Length), alpha = 0.5)

ggplot2::aes、中に書くか外に書くか?(その2)

iris %>% ggplot2::ggplot(ggplot2::aes(x = Species, fill = Species)) + 
  ggplot2::geom_boxplot(ggplot2::aes(y = Sepal.Length, alpha = 0.5))

サイズやシンボル(シェイプ)を変える

iris %>% ggplot2::ggplot(ggplot2::aes(x = Petal.Width, y = Petal.Length)) + 
  ggplot2::geom_point(ggplot2::aes(colour = Species, size = Sepal.Length, shape = Species))

flexdashborad

flexdashboradの区切りはMarkdownのHeader記述(###)で代用できます。幅は合計が1,000になるようにするといい塩梅です。メニューは#レベルを記述すれば自動的にメニューとなり、タブはカラムやローのレベル(##)で明示的に指定する必要があります。 テンプレート例

# Menu (Tab)

## Coloumn Left {data-width=650}

### Component

## Coloumn Rigth {data-width=350} {.tabset}

### Tab 1

### Tab 2

htmlwidgets

htmlwidgets はHTMLドキュメントでJavaScriptを用いてインタラクティブな表示を提供するためのベースとなるパッケージです。htmlwidgetsパッケージを利用した様々なパッケージが公開されています。代表的なパッケージには以下のようなものがあります。

package description
DT DataTablesライブラリを用いたテーブル(表)表示
plotly ggplot2オブジェクトを簡単にインタラクティブ化
dygraphs dygraphsライブライを用いた時系列グラフに特化したパッケージ

Package DT

mtcars %>% DT::datatable(options = list(pageLength = 5))

 

Package plotly

(iris %>% ggplot2::ggplot(ggplot2::aes(x = Petal.Width, y = Petal.Length)) +
           ggplot2::geom_point(ggplot2::aes(colour = Species))) %>% plotly::ggplotly()

Packages dygraphs

xts::as.xts(nhtemp) %>% dygraphs::dygraph(main = "New Haven Temperatures") %>% 
  dygraphs::dyRangeSelector()

Visualizeあれこれ

Communicate

R Markdown

  • Rのコードや実行結果を文書に埋め込めるMarkdown記法
  • 実行結果を直接レポートにできる仕組みは他言語には(ほぼ)ない
    • 例外:Jupyter Notebook(旧 IPython Notebook)
      • これに触発されたのがR Notebook機能と思われる
  • Markdownとは?
    • プレーンテキストで体裁のある文書を作成するための記法
    • シンプル&ライトウェイト、テキストなのでVCSで管理可能
    • 変換ソフト(pandocなど)の利用で多種多様なフォーマットに変換可能
  • シンプルな記述
    • YAML(体裁、出力フォーマットを指定する箇所)
    • (Code) Chunk(実行させたいコードを記述する箇所)
    • Text (Markdown)(コード以外の文書を記述する箇所)

R Markdown Website & blogdown

  • R Markdownを利用して静的サイトを生成する静的サイトジェネレーター
    • R Markdownによる記述
    • knit結果を公開すればWebサイトやblogサイトが簡単に構築できる
  • R Markdown website
    • R MarkdownのHTML出力にメニューを付加するイメージ
    • Bootswatchを用いたテーマ設定が可能
    • 作るのは簡単だけどページ構成管理が手動なのが厄介
  • blogdown
    • 静的サイトジェネレーター(hugo)のラッパーとRStudio Addins
    • websiteより洗練されたデザインテンプレートでページ構成管理は自動
    • テーマ関連の管理がちょっと厄介(Windowsだと展開できないとか脆弱性管理とか)

df_printオプション

---
output: 
  html_document:
    df_print: default         # データフレーム出力フォーマットはここで指定
---
value output
default Rのコンソールでdata.frameを表示した時に使われるテキスト表形式
kable シンプルな表形式(全て表示されるのでhead関数などで制限要)
paged ページ送りのついたインタラクティブな表形式(htmlwidgetsと相性いまいち)
tibble tibbleクラスを用いた簡易なテキスト表形式

おわりに

「データは真実」はほんとうか

Exploratory CEOのnote から

  • 統計の知識を持ってない人がデータを振りかざしてあたかも事実のようにふれ回るのは害悪でしかない(部分意訳)
  • 英語なんかよりも、もっと重要なスキルであるという認識で、統計、機械学習といった知識とスキルを2019年こそは習得していっていただければと(抜粋)

復習のためのリソース

身につけるにはアウトプットが大切です。間違っていても構わないのでアウトプットし続ける癖をつけましょう。

  • 2018年度勉強会
    • 今年度の勉強会の資料はソースも含めて全てこちらで公開しています
    • packratの設定を変えましたので必要なら再クローンを
  • Project Cabinet
    • 環境構築(除くmac OS)と基本事項の説明はこちらから
  • Project Cabinet Blog
    • 上記以外で関数の使い方やTipsなどを紹介しています
  • r-wakalang
    • Rで分からないことは親切なエキスパートが揃ったSlack Communityへ

アウトプットし続けるために

2019年度も快適なコミュニケーションスペースである bit & innovation での勉強会を実施できる運びとなりました!
 
株式会社TISの関係各位に感謝申し上げます。  

  • 参加者全員がアウトプットできるように輪講形式での実施を考えています
  • 詳細は データ分析勉強会サイト で公開いたしますので奮ってご参加ください
 
以上を持ちまして「モダンなRの世界」は終了となります。長い期間ありがとうございました。引き続き関連講義となります。
 
次回(2月)もあるよ!

 

Enjoy !   CC BY-NC-SA 4.0, Sampo